package org.cocos2d.events;

import android.view.KeyEvent;

import org.cocos2d.protocols.CCKeyDelegateProtocol;
import org.cocos2d.utils.collections.ConcNodeCachingLinkedQueue;

import java.util.ArrayList;

public class CCKeyDispatcher {
    public static final boolean kEventHandled = true;
    public static final boolean kEventIgnored = false;
    private static CCKeyDispatcher _sharedDispatcher = new CCKeyDispatcher();
    private final ConcNodeCachingLinkedQueue<KeyEvent> eventQueue = new ConcNodeCachingLinkedQueue<KeyEvent>();
    private ArrayList<CCKeyHandler> keyHandlers;
    private boolean dispatchEvents;

    public CCKeyDispatcher() {
        dispatchEvents = true;
        keyHandlers = new ArrayList<CCKeyHandler>();
    }

    public static CCKeyDispatcher sharedDispatcher() {
        return _sharedDispatcher;
    }

    public boolean getDispatchEvents() {
        return dispatchEvents;
    }

    public void setDispatchEvents(boolean b) {
        dispatchEvents = b;
    }

    private void addHandler(CCKeyHandler handler) {
        int i = 0;

        synchronized (keyHandlers) {
            for (int ind = 0; ind < keyHandlers.size(); ind++) {
                CCKeyHandler h = keyHandlers.get(ind);
                if (h.getPriority() < handler.getPriority())
                    i++;
                if (h.getDelegate() == handler.getDelegate())
                    return;
            }
            keyHandlers.add(i, handler);
        }
    }

    public void addDelegate(CCKeyDelegateProtocol delegate, int prio) {
        addHandler(new CCKeyHandler(delegate, prio));
    }

    public void removeDelegate(CCKeyDelegateProtocol delegate) {
        if (delegate == null)
            return;
        synchronized (keyHandlers) {
            for (int ind = 0; ind < keyHandlers.size(); ind++) {
                CCKeyHandler handler = keyHandlers.get(ind);
                if (handler.getDelegate() == delegate) {
                    keyHandlers.remove(handler);
                    break;
                }
            }
        }
    }

    public void removeAllDelegates() {
        keyHandlers.clear();
    }

    public void queueMotionEvent(KeyEvent event) {
        // copy event for queue
        KeyEvent eventForQueue = new KeyEvent(event);

        eventQueue.push(eventForQueue);
    }

    public void update() {
        KeyEvent event;
        while ((event = eventQueue.poll()) != null) {
            switch (event.getAction()) {
                case KeyEvent.ACTION_DOWN:
                    onKeyDown(event);
                    break;
                case KeyEvent.ACTION_UP:
                    onKeyUp(event);
                    break;
            }
        }
    }

    public void onKeyDown(KeyEvent event) {
        if (dispatchEvents) {

            synchronized (keyHandlers) {
                for (int ind = 0; ind < keyHandlers.size(); ind++) {
                    CCKeyHandler handler = keyHandlers.get(ind);
                    if (handler.ccKeyDown(event.getKeyCode(), event) == kEventHandled)
                        break;
                }
            }
        }
    }

    public void onKeyUp(KeyEvent event) {
        if (dispatchEvents) {
            synchronized (keyHandlers) {
                for (int ind = 0; ind < keyHandlers.size(); ind++) {
                    CCKeyHandler handler = keyHandlers.get(ind);
                    if (handler.ccKeyUp(event.getKeyCode(), event) == kEventHandled)
                        break;
                }
            }
        }
    }
}
